using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using 数据挖掘实验.Logger;

namespace 数据挖掘实验
{
    public class AprioriCollector : IEnumerable<AprioriItem>
    {
        /// <summary>
        /// 所有事务
        /// </summary>
        List<string> transaction = new List<string>();
        /// <summary>
        /// 所有条目
        /// </summary>
        List<HashSet<int>> entries = new List<HashSet<int>>();
        /// <summary>
        /// 索引器
        /// </summary>
        Dictionary<int, AprioriItem> indicator = new Dictionary<int, AprioriItem>();

        float supportThreshold;
        float confidenceThreshold;

        public string[] Titles { get; private set; }
        public List<string> Transaction => transaction;

        public Dictionary<int, AprioriItem>.KeyCollection Keys => indicator.Keys;

        public AprioriCollector() : this(0, 0)
        {
        }

        public AprioriCollector(float supportThreshold, float confidenceThreshold)
        {
            this.supportThreshold = supportThreshold;
            this.confidenceThreshold = confidenceThreshold;
        }

        public void SetThreshold(float supportThreshold, float confidenceThreshold)
        {
            this.supportThreshold = supportThreshold;
            this.confidenceThreshold = confidenceThreshold;
        }

        public void Open(string path)
        {
            transaction.Clear();
            entries.Clear();
            using var reader = new StreamReader(File.Open(path, FileMode.Open), Encoding.GetEncoding("GB2312"));
            string line;
            Titles = reader.ReadLine().Split('\t');

            var transactionSet = new SortedSet<string>();
            var preEntries = new List<HashSet<string>>();
            while ((line = reader.ReadLine()) != null) // 循环读取样本信息
            {
                string[] items = line.Split('\t')[1].Split(',');
                var entry = new HashSet<string>(); // 一个项集
                foreach (var item in items)
                {
                    var temp = item.Trim();
                    entry.Add(temp);
                    transactionSet.Add(temp);
                }

                preEntries.Add(entry); // 存储项集
            }

            transaction = transactionSet.ToList(); // 最终得到的事务集
            entries = preEntries.Select(preEntry => preEntry.Select(ToTransactionIndex).ToHashSet()).ToList();
        }

        int ToTransactionIndex(string item)
        {
            return transaction.IndexOf(item);
        }

        public void Compute()
        {
            indicator.Clear();
            ComputeSupport();
            ComputeConfidence();
        }

        void ComputeConfidence()
        {
            // 计算置信度
            LogManager.AprioriLogger.Log($"==============开始计算置信度==============\n");
            foreach (var item in indicator.Values)
            {
                foreach (var nextItem in item)
                {
                    nextItem.ComputeConfidence(this, transaction, confidenceThreshold);
                }
            }
        }

        void ComputeSupport()
        {
            // 计算支持度 并进行计数
            LogManager.AprioriLogger.Log($"==============开始计算支持度==============\n");
            int depth = 1;
            var pendingComputeTask = new Queue<Action>();
            var continuousSectionIndex = transaction.Select(x => transaction.IndexOf(x));
            for (var i = 0; i < transaction.Count; i++)
            {
                var aprioriItem = new AprioriItem(i); // 一个关联规则项
                if (aprioriItem.ComputeSupport(i + 1, depth, this, pendingComputeTask, transaction,continuousSectionIndex, entries,
                    supportThreshold)) // 进行访问计算
                    indicator.Add(i, aprioriItem); // 达到支持度阈值则进行存储
            }

            while (pendingComputeTask.TryDequeue(out var task))
                task.Invoke();
        }

        /// <summary>
        /// 使用键进行索引
        /// </summary>
        /// <param name="key">键</param>
        public AprioriItem this[int key]
        {
            get
            {
                if (indicator.TryGetValue(key, out var item))
                    return item;
                return null;
            }
        }

        /// <summary>
        /// 使用迭代器进行逐层索引
        /// </summary>
        /// <param name="keyEnumrator">迭代器</param>
        public AprioriItem this[IEnumerable<int> keyEnumrator]
        {
            get
            {
                if (keyEnumrator == null)
                {
                    return null;
                }

                AprioriItem item = null;
                foreach (var key in keyEnumrator)
                {
                    if (item == null)
                        item = this[key];
                    else
                        item = item[key];
                }

                return item;
            }
        }

        public IEnumerator<AprioriItem> GetEnumerator()
        {
            foreach (var item in indicator.Values)
            {
                yield return item;
                foreach (var childItem in item)
                {
                    yield return childItem;
                }
            }

        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}